package model;

import controller.Controller;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;


/**
 * This class is used as the Model in the MVC
 * Changed parse() using reflection with exception handling - Rex
 * 
 * @author Bruce Fu, edited extensively by Tommy Petrilak
 * 
 */
public class DataLoader {

    private final String myParserClassDirectory = "model.";

    private Table myTable = null;
    private List<String> myFieldNames = new ArrayList<String>();
    private Controller myController;
    private String myFileNameExtension;

    private final String myExtensionNotSupportedError =
            "The extension of the file selected is not supported :(";
    private final String myParserConstructionError =
            "Error when constructing a parser for a supported extension!";
    private final String myReadingFileError = "Error when reading files";

    /**
     * construct DataLoader
     * 
     * @param controller Controller instance (used only for getting data for GUI
     *        message)
     */
    public DataLoader (Controller controller) {
        myFileNameExtension = null;
        myController = controller;
    }

    /**
     * Load data from file, choosing which parser to use based on file
     * extension.
     * 
     * @param file chosen by user
     */
    public boolean parse (File file) {
        myFileNameExtension = getFileNameExtension(file.getName()).toUpperCase();
        Parser myParser = null;
        boolean myIsParsed = false;
        @SuppressWarnings("rawtypes")
        Constructor parserConstructor = null;
        try {
            parserConstructor = Class.forName(
                    myParserClassDirectory + myFileNameExtension +
                    "Parser").getConstructor(File.class);
            myIsParsed = true;
        }
        catch (ClassNotFoundException e) {
            myController.showError(myExtensionNotSupportedError);
        }
        catch (NoSuchMethodException e) {  
            System.err.println("No such constructor");
        }
        if (!myIsParsed) { return false;}
        myIsParsed = false;

        try {
            myParser = (Parser) (parserConstructor.newInstance(file));
            myIsParsed = true;
        }
        catch (InstantiationException e) {
            System.err.println(myParserConstructionError);
            e.printStackTrace();
        }
        catch (IllegalAccessException e) {  
            System.err.println(myFileNameExtension + " should be made public");
        }
        catch (IllegalArgumentException e) {  
            System.err.println("Not a file!");
            e.printStackTrace();
        }
        catch (InvocationTargetException e) {  
            e.printStackTrace();
        }
        if (!myIsParsed) { return false;}
        myIsParsed = false;

        try {
            myParser.loadFile(file);
            myTable = myParser.retrieveTable();
            myFieldNames = myParser.getFieldNames();
            myIsParsed = true;
        }
        catch (IOException e) {
            System.err.println(myReadingFileError);
            e.printStackTrace();
        }
        return myIsParsed;
    }

    private String getFileNameExtension (String fileName) {
        if ((fileName != null) && (fileName.length() > 0)) {
            int i = fileName.lastIndexOf('.');

            if ((i > -1) && (i < (fileName.length() - 1))) { return fileName.substring(i + 1); }
        }
        return null;
    }

    /**
     * get category names
     * 
     * @return an array of category names
     */
    public String[] getCategories () {
        String[] result = new String[myFieldNames.size()];
        for (int i = 0; i < result.length; i++) {
            result[i] = myFieldNames.get(i);
        }
        return result;
    }

    /**
     * Get a sub-table containing columns specified in the categories
     * 
     * @param categories fetch data in these categories
     * 
     * @return sub-table to be displayed
     */
    @SuppressWarnings("rawtypes")
    public Table getSubTable (String[] categories) {
        List<ArrayList<Comparable>> subTable = new ArrayList<ArrayList<Comparable>>();
        List<String> subFieldTypes = new ArrayList<String>();

        int[] columnIndices = new int[categories.length + 1];
        columnIndices[0] = 0;
        subFieldTypes.add("String");
        int count = 1;
        for (String categorie : categories) {
            if (myFieldNames.contains(categorie)) {
                columnIndices[count] = myFieldNames.indexOf(categorie);
                subFieldTypes.add(myTable.getFieldTypes().get(
                        columnIndices[count]));
                count++;
            }
        }

        for (int i = 0; i < myTable.getDimension().height; i++) {
            ArrayList<Comparable> rowData = new ArrayList<Comparable>();
            for (int j = 0; j < count; j++) {
                rowData.add(myTable.getData(i, columnIndices[j]).getValue());
            }
            subTable.add(rowData);
        }

        Table resultTable = new Table(subTable, subFieldTypes);
        return resultTable;
    }
}
